import {Note} from '../_component/note.jsx'

export const info = {
  author: [
    {github: 'wooorm', name: 'Titus Wormer'}
  ],
  modified: new Date('2025-01-27'),
  published: new Date('2021-09-30')
}
export const navSortSelf = 3

# Using MDX

This article explains how to use MDX files in your project.
It shows how you can pass props and how to import, define, or pass components. {/* more */}
See [§ Getting started][start] for how to integrate MDX into your project.
To understand how the MDX format works, we recommend that you start with
[§ What is MDX][what].

## Contents

* [How MDX works](#how-mdx-works)
* [MDX content](#mdx-content)
  * [Props](#props)
  * [Components](#components)
  * [Layout](#layout)
* [MDX provider](#mdx-provider)

## How MDX works

An integration compiles MDX syntax to JavaScript.
Say we have an MDX document, `example.mdx`:

```mdx path="input.mdx"
export function Thing() {
  return <>World</>
}

# Hello <Thing />
```

That’s *roughly* turned into the following JavaScript.
The below might help to form a mental model:

```jsx twoslash path="output-outline.jsx"
/* @jsxRuntime automatic */
/* @jsxImportSource react */

export function Thing() {
  return <>World</>
}

export default function MDXContent() {
  return <h1>Hello <Thing /></h1>
}
```

Some observations:

* The output is serialized JavaScript that still needs to be evaluated
* A comment is injected to configure how JSX is handled
* It’s a complete file with import/exports
* A component (`MDXContent`) is exported

The *actual* output is:

```js twoslash path="output-actual.js"
// @noErrors
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'

export function Thing() {
  return _jsx(_Fragment, {children: 'World'})
}

function _createMdxContent(props) {
  const _components = {h1: 'h1', ...props.components}
  return _jsxs(_components.h1, {children: ['Hello ', _jsx(Thing, {})]})
}

export default function MDXContent(props = {}) {
  const {wrapper: MDXLayout} = props.components || {}
  return MDXLayout
    ? _jsx(MDXLayout, {...props, children: _jsx(_createMdxContent, {...props})})
    : _createMdxContent(props)
}
```

Some more observations:

* JSX is compiled away to function calls and an import of React†
* The content component can be given `{components: {wrapper: MyLayout}}` to
  wrap all content
* The content component can be given `{components: {h1: MyComponent}}` to use
  something else for the heading

† MDX is not coupled to React.
You can also use it with [Preact][],
[Vue][], [Emotion][],
[Theme UI][],
etc.
Both the classic and automatic JSX runtimes are supported.

## MDX content

We just saw that MDX files are compiled to components.
You can use those components like any other component in your framework of
choice.
Take this file:

```mdx path="example.mdx"
# Hi!
```

It could be imported and used in a React app like so:

```jsx twoslash path="example.jsx"
// @filename: types.d.ts
import type {} from 'mdx'
// @filename: example.jsx
/// <reference lib="dom" />
// ---cut---
import {createRoot} from 'react-dom/client'
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.

const container = document.getElementById('root')
if (!container) throw new Error('Expected `root`')
const root = createRoot(container)
root.render(<Example />)
```

The main content is exported as the default export.
All other values are also exported.
Take this example:

```mdx path="example.mdx"
export function Thing() {
  return <>World</>
}

# Hello <Thing />
```

It could be imported in the following ways:

```js twoslash path="example.js"
// @filename: types.d.ts
declare module '*.mdx' {
  export {MDXContent as default} from 'mdx/types';
  export function Thing(): unknown;
}
// @filename: example.js
/// <reference types="node" />
// ---cut---
// A namespace import to get everything:
import * as everything from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
console.log(everything) // {Thing: [Function: Thing], default: [Function: MDXContent]}

// Default export shortcut and a named import specifier:
import Content, {Thing} from './example.mdx'
console.log(Content) // [Function: MDXContent]
console.log(Thing) // [Function: Thing]

// Import specifier with another local name:
import {Thing as AnotherName} from './example.mdx'
console.log(AnotherName) // [Function: Thing]
```

### Props

In [§ What is MDX][what], we showed that JavaScript expressions, inside curly
braces, can be used in MDX:

```mdx path="example.mdx"
import {year} from './data.js'
export const name = 'world'

# Hello {name.toUpperCase()}

The current year is {year}
```

Instead of importing or defining data within MDX, data can also be passed
to `MDXContent`.
The passed data is called `props`.
Take for example:

```mdx path="example.mdx"
# Hello {props.name.toUpperCase()}

The current year is {props.year}
```

This file could be used as:

```jsx twoslash path="example.jsx"
// @filename: types.d.ts
import type {} from 'mdx'
// @filename: example.jsx
/// <reference lib="dom" />
// ---cut---
import React from 'react'
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.

// Use a `createElement` call:
console.log(React.createElement(Example, {name: 'Venus', year: 2021}))

// Use JSX:
console.log(<Example name="Mars" year={2022} />)
```

<Note type="info">
  **Note**:
  Users of the MDX VS Code extension can add type
  checking of `props` with a JSDoc comment.
  See [`mdx-js/mdx-analyzer`][mdx-analyzer] for more info.
</Note>

### Components

There is one special prop: `components`.
It takes an object mapping component names to components.
Take this example:

```mdx path="example.mdx"
# Hello *<Planet />*
```

It can be imported from JavaScript and passed components like so:

```jsx twoslash path="example.jsx"
// @filename: types.d.ts
import type {} from 'mdx'
// @filename: example.jsx
/// <reference lib="dom" />
/* @jsxImportSource react */
// ---cut---
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.

console.log(
  <Example
    components={{
      Planet() {
        return <span style={{color: 'tomato'}}>Pluto</span>
      }
    }}
  />
)
```

You don’t have to pass components.
You can also define or import them within MDX:

```mdx path="example.mdx"
import {Box, Heading} from 'rebass'

MDX using imported components!

<Box>
  <Heading>Here’s a heading</Heading>
</Box>
```

Because MDX files *are* components, they can also import each other:

```mdx path="example.mdx"
import License from './license.md' // Assumes an integration is used to compile markdown -> JS.
import Contributing from './docs/contributing.mdx'

# Hello world

<License />

---

<Contributing />
```

Here are some other examples of passing components:

```jsx twoslash path="example.jsx"
// @filename: types.d.ts
import type {} from 'mdx'
// @filename: example.jsx
/// <reference lib="dom" />
/* @jsxImportSource react */
import Example from './example.mdx'
// @errors: 2322 -- something with React 19 and nested components.
// ---cut---
console.log(
  <Example
    components={{
      // Map `h1` (`# heading`) to use `h2`s.
      h1: 'h2',
      // Rewrite `em`s (`*like so*`) to `i` with a goldenrod foreground color.
      em(props) {
        return <i style={{color: 'goldenrod'}} {...props} />
      },
      // Pass a layout (using the special `'wrapper'` key).
      wrapper({components, ...rest}) {
        return <main {...rest} />
      },
      // Pass a component.
      Planet() {
        return 'Neptune'
      },
      // This nested component can be used as `<theme.text>hi</theme.text>`
      theme: {
        text(props) {
          return <span style={{color: 'grey'}} {...props} />
        }
      }
    }}
  />
)
```

The following keys can be passed in `components`:

* HTML equivalents for the things you write with markdown such as `h1` for
  `# heading` (see [§ Table of components][toc] for examples)
* `wrapper`, which defines the layout (but a local layout takes precedence)
* anything else that is a valid JSX identifier (`foo`, `Quote`,
  `custom-element`, `_`, `$x`, `a1`) for the things you write with JSX (like
  `<So />` or `<like.so />`, note that locally defined components take
  precedence)**‡**

**‡** The rules for whether a name in JSX (so `x` in `<x>`) is a literal tag
name (like `h1`) or not (like `Component`) are as follows:

* if there’s a dot,
  it’s a member expression (`<a.b>` → `h(a.b)`),
  which means a reference to the key `b` taken from object `a`
* otherwise,
  if the name is not a valid JS identifier,
  it’s a literal (`<a-b>` → `h('a-b')`)
* otherwise,
  if it starts with a lowercase,
  it’s a literal (`<a>` → `h('a')`)
* otherwise,
  it’s a reference (`<A>` → `h(A)`)

These keys in `components` and the difference between literal tag names and
references is illustrated as follows.
With the following MDX:

```mdx path="example.mdx"
* [markdown syntax](#alpha)
* <a href="#bravo">JSX with a lowercase name</a>
* <Link to="#charlie">JSX with a capitalized name</Link>
```

…passed some components:

```jsx twoslash path="example.jsx"
// @filename: types.d.ts
import type {} from 'mdx'
// @filename: example.jsx
/// <reference lib="dom" />
/* @jsxImportSource react */
// ---cut---
import Example from './example.mdx'

console.log(
  <Example
    components={{
      a(props) {
        return <a {...props} style={{borderTop: '1px dotted', color: 'violet'}} />
      },
      Link(props) {
        return <a href={props.to} children={props.children} style={{borderTop: '1px dashed', color: 'tomato'}} />
      }
    }}
  />
)
```

…we’d get:

{
  <div className="card">
    <ul>
      <li><a href="#alpha" style={{borderTop: '1px dotted', color: 'violet'}}>markdown syntax</a></li>
      <li><a href="#bravo">JSX with a lowercase name</a></li>
      <li><a href="#charlie" style={{borderTop: '1px dashed', color: 'tomato'}}>JSX with a capitalized name</a></li>
    </ul>
  </div>
}

Observe that the first link (`#alpha`) is dotted and violet.
That’s because `a` is the HTML equivalent for the markdown syntax being used.
The second link (`#bravo`) remains unchanged,
because in JSX syntax `a` is a literal tag name.
The third link (`#charlie`) is dashed and tomato,
because in JSX syntax `Link` is a reference.

### Layout

There is one special component: the layout.
If it is defined, it’s used to wrap all content.
A layout can be defined from within MDX using a default export:

```mdx
export default function Layout({children}) {
  return <main>{children}</main>;
}

All the things.
```

The layout can also be imported and *then* exported with an `export … from`:

```mdx
export {Layout as default} from './components.js'
```

The layout can also be passed as `components.wrapper` (but a local one takes
precedence).

## MDX provider

You probably don’t need a provider.
Passing components is typically fine.
Providers often only add extra weight.
Take for example this file:

```mdx path="post.mdx"
# Hello world
```

Used like so:

```jsx twoslash path="app.jsx"
// @filename: components.d.ts
import React from 'react'
declare const Heading: {H1: React.ComponentType}
declare const Table: React.ComponentType
// @filename: types.d.ts
import type {} from 'mdx'
// @filename: example.jsx
/// <reference lib="dom" />
// ---cut---
import {createRoot} from 'react-dom/client'
import {Heading, /* … */ Table} from './components.js'
import Post from './post.mdx' // Assumes an integration is used to compile MDX -> JS.

const components = {
  h1: Heading.H1,
  // …
  table: Table
}

const container = document.getElementById('root')
if (!container) throw new Error('Expected `root`')
const root = createRoot(container)
root.render(<Post components={components} />)
```

That works, those components are used.

But when you’re nesting MDX files (importing them into each other) it can become
cumbersome.
Like so:

```mdx path="post.mdx"
import License from './license.md' // Assumes an integration is used to compile markdown -> JS.
import Contributing from './docs/contributing.mdx'

# Hello world

<License components={props.components} />

---

<Contributing components={props.components} />
```

To solve this, a *[context][]* can be used in React, Preact, and Vue.
Context provides a way to pass data through the component tree without having to
pass props down manually at every level.
Set it up like so:

1. Install either [`@mdx-js/react`][mdx-react], [`@mdx-js/preact`][mdx-preact],
   or [`@mdx-js/vue`][mdx-vue],
   depending on what framework you’re using
2. Configure your MDX integration with
   [`providerImportSource` in `ProcessorOptions`][processor-options]
   set to that package, so either `'@mdx-js/react'`, `'@mdx-js/preact'`, or
   `'@mdx-js/vue'`
3. Import `MDXProvider` from that package.
   Use it to wrap your top-most MDX content component and pass it your
   `components` instead:

```diff
+import {MDXProvider} from '@mdx-js/react'
 import {createRoot} from 'react-dom/client'
 import {Heading, /* … */ Table} from './components/index.js'
 import Post from './post.mdx' // Assumes an integration is used to compile MDX -> JS.
@@ -13,4 +14,8 @@ const components = {

 const container = document.getElementById('root')
 if (!container) throw new Error('Expected `root`')
 const root = createRoot(container)
-root.render(<Post components={components} />)
+root.render(
+  <MDXProvider components={components}>
+    <Post />
+  </MDXProvider>
+)
```

Now you can remove the explicit and verbose component passing:

```diff
 import License from './license.md' // Assumes an integration is used to compile markdown -> JS.
 import Contributing from './docs/contributing.mdx'

 # Hello world

-<License components={props.components} />
+<License />

 ---

-<Contributing components={props.components} />
+<Contributing />
```

When `MDXProvider`s are nested, their components are merged.
Take this example:

```jsx twoslash
// @filename: types.d.ts
import React from 'react'
import type {MDXContent} from 'mdx/types.js'
declare const Component1: React.ComponentType
declare const Component2: React.ComponentType
declare const Component3: React.ComponentType
declare const Component4: React.ComponentType
declare const Content: MDXContent
// @filename: example.jsx
/// <reference lib="dom" />
import {MDXProvider} from '@mdx-js/react'
import React from 'react'
import {Component1, Component2, Component3, Component4, Content} from './types.js'
// ---cut---
console.log(
  <MDXProvider components={{h1: Component1, h2: Component2}}>
    <MDXProvider components={{h2: Component3, h3: Component4}}>
      <Content />
    </MDXProvider>
  </MDXProvider>
)
```

…which results in `h1`s using `Component1`, `h2`s using `Component3`, and `h3`s
using `Component4`.

To merge differently or not at all, pass a function to `components`.
It’s given the current context `components` and what it returns will be used
instead.
In this example the current context components are discarded:

```jsx twoslash
// @filename: types.d.ts
import React from 'react'
import type {MDXContent} from 'mdx/types.js'
declare const Component1: React.ComponentType
declare const Component2: React.ComponentType
declare const Component3: React.ComponentType
declare const Component4: React.ComponentType
declare const Content: MDXContent
// @filename: example.jsx
/// <reference lib="dom" />
import {MDXProvider} from '@mdx-js/react'
import React from 'react'
import {Component1, Component2, Component3, Component4, Content} from './types.js'
// ---cut---
console.log(
  <MDXProvider components={{h1: Component1, h2: Component2}}>
    <MDXProvider
      components={
        function () {
          return {h2: Component3, h3: Component4}
        }
      }
    >
      <Content />
    </MDXProvider>
  </MDXProvider>
)
```

…which results in `h2`s using `Component3` and `h3`s using `Component4`.
No component is used for `h1`.

If you’re not nesting MDX files, or not nesting them often, don’t use
providers: pass components explicitly.

[context]: https://reactjs.org/docs/context.html

[emotion]: /docs/getting-started/#emotion

[mdx-analyzer]: https://github.com/mdx-js/mdx-analyzer

[mdx-preact]: /packages/preact/

[mdx-react]: /packages/react/

[mdx-vue]: /packages/vue/

[preact]: /docs/getting-started/#preact

[processor-options]: /packages/mdx/#processoroptions

[start]: /docs/getting-started/

[theme ui]: /docs/getting-started/#theme-ui

[toc]: /table-of-components/

[vue]: /docs/getting-started/#vue

[what]: /docs/what-is-mdx/
